//Copyright (C) 2007 James Ashley (www.imaginativeuniversal.com) under the terms of the LGPL.
using System;
using System.Collections.Generic;
using System.Speech.Recognition;
using System.Text;
using System.Xml;
using System.IO;
using AIMLbot;
using System.Threading;

namespace SophiaBot
{
    public class AIMLBotAdapter: BotServerBase
    {

        #region local fields

        protected string _aIMLFolderPath = string.Empty;
        protected List<Grammar> _aIMLGrammar = new List<Grammar>();
        protected AIMLbot.User _myUser = null;
        protected Bot _myBot = null;
        private object myLock = new object();
        private object propertyLock = new object();
        private bool _isGrammarRequested = false;

        #endregion

        #region constructor

        public AIMLBotAdapter()
        {
            _name = "AIML Bot Adapter";
            //bot code
            _myBot = new Bot();
            _myBot.loadSettings();
            _myUser = new User("DefaultUser", this._myBot);
        }

        public AIMLBotAdapter(string aIMLFolderPath): this()
        {
            _aIMLFolderPath = aIMLFolderPath;
            LoadAIMLFiles(_aIMLFolderPath, this._myBot);
        }

        #endregion

        #region IBotServer Members

        /// <summary>
        /// Reads the specified input text.
        /// </summary>
        /// <param name="inputText">The input text.</param>
        public override void Read(string inputText)
        {
            if (_isRunning)
            {
                //process as a unit
                lock (staticLock)
                {
                    RaiseUserInputEvent(FormatInputForDisplay(inputText));
                    Request myRequest = new Request(inputText, this._myUser, this._myBot);
                    Result myResult = this._myBot.Chat(myRequest);
                    RaiseBotResponseEvent(this, myResult.Output);
                }
            }
            else
            {
                if (null != _nextBot)
                    _nextBot.Read(inputText);
            }
        }

        /// <summary>
        /// Gets the grammar.
        /// </summary>
        /// <returns></returns>
        public override Grammar[] GetGrammars()
        {
            IsGrammarRequested = true;
            LoadAIMLGrammars();
            return _aIMLGrammar.ToArray();
        }


        /// <summary>
        /// Starts the specified val.
        /// </summary>
        /// <param name="val">The val.</param>
        public override void Start(string val)
        {
            Start();
            //if (System.Environment.OSVersion.Version.Major >= 6)
            //{
            //    Thread backgroundThread = new Thread(new ThreadStart(LoadAIMLGrammars));
            //    backgroundThread.Name = "Load AIML Grammars in background";
            //    backgroundThread.IsBackground = true;
            //    backgroundThread.Start();
            //}
        }

        #endregion

        #region helper methods and properties

        /// <summary>
        /// Loads the AIML files.
        /// </summary>
        /// <param name="aIMLFolderPath">AIML folder path.</param>
        /// <param name="bot">The bot that will process AIML files found in the AIML folder path.</param>
        protected virtual void LoadAIMLFiles(string aIMLFolderPath, Bot bot)
        {
            AIMLbot.Utils.AIMLLoader loader = new AIMLbot.Utils.AIMLLoader(bot);
            this._myBot.isAcceptingUserInput = false;
            if (aIMLFolderPath.Length > 0)
            {
                loader.loadAIML(aIMLFolderPath);
            }
            bot.isAcceptingUserInput = true;
        }

        /// <summary>
        /// Loads the AIML grammars.
        /// </summary>
        protected virtual void LoadAIMLGrammars()
        {
            lock (myLock)
            {
                if (_aIMLGrammar.Count == 0)
                    CreateAIMLGrammars();
            }
        }

        
        /// <summary>
        /// Creates the AIML grammar builders.
        /// </summary>
        /// <param name="aIMLFolderPath">AIML folder path.</param>
        protected virtual void CreateAIMLGrammars()
        {
            XmlDocument doc = null;
            string[] fileNames = Directory.GetFiles(_aIMLFolderPath);
            int i = 0;
            foreach (string fileName in fileNames)
            {
                if (fileName.Substring(fileName.Length - 5).ToLower() != ".aiml")
                    continue;

                doc = new XmlDocument();
                doc.Load(fileName);
                Choices c = new Choices();
                XmlNodeList rootChildren = doc.DocumentElement.SelectNodes("/aiml/category/pattern");
                foreach (XmlNode pattern in rootChildren)
                {
                    string patternText = pattern.InnerText.ToUpper();
                    if (patternText.IndexOf("\"") > -1)
                        continue;
                    if (patternText.IndexOf("*") > -1)
                    {
                        BuildDictationWildcardChoices(c, patternText);
                    }
                    else
                    {
                        c.Add(patternText);
                    }
                }
                Grammar g = new Grammar(new GrammarBuilder(c));
                g.Name = this._name;
                g.Priority = this._priority;
                g.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(AIMLGrammar_SpeechRecognized);
                _aIMLGrammar.Add(g);
                i++;
                DebugWriteToConsole(i.ToString() + " of " + fileNames.Length.ToString() + " AIML grammar files processed...");
                if (null != OnUpdateLoadStatus)
                {
                    OnUpdateLoadStatus("Sorry I'm running so slow.  Please be patient.  " + i.ToString() + " of " + fileNames.Length.ToString() + " AIML grammar files processed...");
                }
            }
        }


        /// <summary>
        /// Builds the dictation wildcard choices.
        /// </summary>
        /// <param name="choices">A speech recognition choices instance.</param>
        /// <param name="wildCardString">The wild card string.</param>
        protected virtual void BuildDictationWildcardChoices(Choices choices, string wildCardString)
        {
            GrammarBuilder gb = new GrammarBuilder();
            char wildCardChar;
            if (wildCardString.IndexOf("_") > -1)
            {
                wildCardChar = '_';
            }
            else
            {
                wildCardChar = '*';
            }
            string[] phraseSegments = wildCardString.Split(wildCardChar);
            if (phraseSegments.Length == 2)
            {
                GrammarBuilder mainphrase = new GrammarBuilder();
                if (phraseSegments[0] == string.Empty && phraseSegments[1] == string.Empty)
                {
                    //wildcard only
                    mainphrase.AppendDictation();
                    gb.Append(mainphrase);
                }
                else if (phraseSegments[0] == string.Empty)
                {
                    //wildcard first
                    GrammarBuilder wildcardBuilder = new GrammarBuilder();
                    wildcardBuilder.AppendDictation();
                    SemanticResultKey wc = new SemanticResultKey("wildcard", wildcardBuilder);
                    mainphrase.Append(wc);
                    mainphrase.Append(phraseSegments[1]);
                    gb.Append(mainphrase);
                }
                else if (phraseSegments[1] == string.Empty)
                {
                    //wildcard last
                    mainphrase.Append(phraseSegments[0]);
                    GrammarBuilder wildcardBuilder = new GrammarBuilder();
                    wildcardBuilder.AppendDictation();
                    SemanticResultKey wc = new SemanticResultKey("wildcard", wildcardBuilder);
                    mainphrase.Append(wc);
                    gb.Append(mainphrase);
                }
                else
                {
                    //wildcard middle
                    mainphrase.Append(phraseSegments[0]);
                    GrammarBuilder wildcardBuilder = new GrammarBuilder();
                    wildcardBuilder.AppendDictation();
                    SemanticResultKey wc = new SemanticResultKey("wildcard", wildcardBuilder);
                    mainphrase.Append(wc);
                    mainphrase.Append(phraseSegments[1]);
                    gb.Append(mainphrase);
                }
            }
            else if (phraseSegments.Length == 1)
            {
                gb.Append(wildCardString);
            }
            choices.Add(gb);
        }

        /// <summary>
        /// Handles the SpeechRecognized event of the AIMLGrammar server.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Speech.Recognition.SpeechRecognizedEventArgs"/> instance containing the event data.</param>
        public virtual void AIMLGrammar_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            string resultText = e.Result.Text;
            RaiseTextRecognizedEvent(resultText);
            DebugWriteToConsole(resultText + " Recognized in AIML bot Grammar");
            resultText = FormatProperCase(resultText);
            Read(resultText);
        }

        #endregion

        #region public methods and properties

        public event GenericEventHandler<string> OnUpdateLoadStatus;

        /// <summary>
        /// Gets or sets the AIML folder path.
        /// </summary>
        /// <value>The AIML folder path.</value>
        protected string AIMLFolderPath
        {
            get { return _aIMLFolderPath; }
            set { _aIMLFolderPath = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is grammar requested.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is grammar requested; otherwise, <c>false</c>.
        /// </value>
        public bool IsGrammarRequested
        {
            get {
                lock (propertyLock)
                {
                    return _isGrammarRequested;
                }
            }
            set {
                lock (propertyLock)
                {
                    _isGrammarRequested = value;
                }
            }
        }

        #endregion
    }
}
